/**********************************************************************************
// SceneManager Source
//
// Creation:	31 Ago 2007
// Updated:		27 Set 2009
// Compiler:	Visual Studio 2008
//
// Notes:		Defines a Scene Manager to deal with game objects
//
**********************************************************************************/

#include "SceneManager.h"

/**********************************************************************************/
/*				              Colision Detection                                  */
/**********************************************************************************/

bool SceneManager::Collision(GameObject& oa, GameObject& ob)
{
	// Point
	if (oa.type == ObjPoint) 
	{
		switch(ob.type)
		{
		case ObjPoint:		
			// Point && Point
			return Collision((Point&) oa, (Point&) ob);  

		case ObjCircle:
			// Point && Circle
			return Collision((Point&) oa, (Circle&) ob); 

		case ObjRectangle:	
			// Point && Rectangle
			return Collision((Point&) oa, (Rect&) ob);   
	
		case ObjPolygon:	
			// Point && Polygon
			return Collision((Point&) oa, (Poly&) ob);

		case ObjSprite:	
			// Sprite && Sprite
			return Collision((Point&) oa, (Sprite&) ob);
			
		default:	
			// Point && Unknown
			return false;
		}
	}

	// Circle
	if (oa.type == ObjCircle) 
	{
		switch(ob.type)
		{
		case ObjPoint:		
			// Circle && Point
			return Collision((Circle&) oa, (Point&) ob);  

		case ObjCircle:
			// Circle && Circle
			return Collision((Circle&) oa, (Circle&) ob); 

		case ObjRectangle:	
			// Circle && Rectangle
			return Collision((Circle&) oa, (Rect&) ob);   
	
		case ObjPolygon:	
			// Circle && Polygon
			return Collision((Circle&) oa, (Poly&) ob);

		case ObjSprite:	
			// Sprite && Sprite
			return Collision((Circle&) oa, (Sprite&) ob);

		default:	
			// Circle && Unknown
			return false;
		}
	}

	// Rectangle
	if (oa.type == ObjRectangle) 
	{
		switch(ob.type)
		{
		case ObjPoint:		
			// Rectangle && Point
			return Collision((Rect&) oa, (Point&) ob);  

		case ObjCircle:
			// Rectangle && Circle
			return Collision((Rect&) oa, (Circle&) ob); 

		case ObjRectangle:	
			// Rectangle && Rectangle
			return Collision((Rect&) oa, (Rect&) ob);   
	
		case ObjPolygon:	
			// Rectangle && Polygon
			return Collision((Rect&) oa, (Poly&) ob);

		case ObjSprite:	
			// Sprite && Sprite
			return Collision((Rect&) oa, (Sprite&) ob);

		default:	
			// Rectangle && Unknown
			return false;
		}
	}

	// Polygon
	if (oa.type == ObjPolygon) 
	{
		switch(ob.type)
		{
		case ObjPoint:		
			// Polygon && Point
			return Collision((Poly&) oa, (Point&) ob);

		case ObjCircle:
			// Polygon && Circle
			return Collision((Poly&) oa, (Circle&) ob); 

		case ObjRectangle:	
			// Polygon && Rectangle
			return Collision((Poly&) oa, (Rect&) ob);  
	
		case ObjPolygon:	
			// Polygon && Polygon
			return Collision((Poly&) oa, (Poly&) ob);

		case ObjSprite:	
			// Sprite && Sprite
			return Collision((Poly&) oa, (Sprite&) ob);

		default:	
			// Polygon && Unknown
			return false;
		}
	}

	// Sprite
	if (oa.type == ObjSprite) 
	{
		switch(ob.type)
		{
		case ObjPoint:		
			// Polygon && Point
			return Collision((Sprite&) oa, (Point&) ob);

		case ObjCircle:
			// Polygon && Circle
			return Collision((Sprite&) oa, (Circle&) ob); 

		case ObjRectangle:	
			// Polygon && Rectangle
			return Collision((Sprite&) oa, (Rect&) ob);  
	
		case ObjPolygon:	
			// Polygon && Polygon
			return Collision((Sprite&) oa, (Poly&) ob);

		case ObjSprite:	
			// Sprite && Sprite
			return Collision((Sprite&) oa, (Sprite&) ob);

		default:	
			// Polygon && Unknown
			return false;
		}
	}

	// first object is unknown
	return false;
}

/**********************************************************************************/

bool SceneManager::Collision(Point& pa, Point& pb)
{
	if (pa.x == pb.x && pa.y == pb.y)
		return true;
	else
		return false;	
}

/**********************************************************************************/

bool SceneManager::Collision(Circle& ca, Circle& cb)
{
	// take the distance between the center points of the circles
	Point pa = Point(ca.x, ca.y);
	Point pb = Point(cb.x, cb.y); 
	float distance = pa.Distance(pb);

	// if distance is less then the sum of the radius
	// then a collision exists between the two circles
	if (distance <= (ca.GetRadius() + cb.GetRadius()))
		return true;

	// no colision detected
	return false;
}

/**********************************************************************************/

bool SceneManager::Collision(Rect& ra, Rect& rb)
{
	// take rectangle screen cordinates
	float aLeft   = ra.GetLeft();
	float aRight  = ra.GetRight();
	float aTop    = ra.GetTop();
	float aBottom = ra.GetBottom();

	float bLeft   = rb.GetLeft();
	float bRight  = rb.GetRight();
	float bTop    = rb.GetTop();
	float bBottom = rb.GetBottom();

	// -------------------------------------
	// Test corners of rectangle A against B
	// -------------------------------------
	// test collision for corner (aLeft,aTop)
	if (aLeft >= bLeft && aLeft <= bRight)
		if (aTop >= bTop && aTop <= bBottom)
			return true;

	// test collision for corner (aRight,aTop)
	if (aRight >= bLeft && aRight <= bRight)
		if (aTop >= bTop && aTop <= bBottom)
			return true;

	// test collision for corner (aRight,aBottom)
	if (aRight >= bLeft && aRight <= bRight)
		if (aBottom >= bTop && aBottom <= bBottom)
			return true;

	// test collision for corner (aLeft,aBottom)
	if (aLeft >= bLeft && aLeft <= bRight)
		if (aBottom >= bTop && aBottom <= bBottom)
			return true;

	// -------------------------------------
	// Test corners of rectangle B against A
	// -------------------------------------
	// test collision for corner (bLeft,bTop)
	if (bLeft >= aLeft && bLeft <= aRight)
		if (bTop >= aTop && bTop <= aBottom)
			return true;

	// test collision for corner (bRight,bTop)
	if (bRight >= aLeft && bRight <= aRight)
		if (bTop >= aTop && bTop <= aBottom)
			return true;

	// test collision for corner (bRight,bBottom)
	if (bRight >= aLeft && bRight <= aRight)
		if (bBottom >= aTop && bBottom <= aBottom)
			return true;

	// test collision for corner (aLeft,aBottom)
	if (bLeft >= aLeft && bLeft <= aRight)
		if (bBottom >= aTop && bBottom <= aBottom)
			return true;

	// no colision detected
	return false;
}

/**********************************************************************************/

bool SceneManager::Collision(Poly& pa, Poly& pb)
{
	// if bounding boxes collide, investigate the poly
	// otherwise there is no collision
	if (Collision(pa.GetBBox(), pb.GetBBox()))
	{

		int nva    = pa.vertexCount;
		Point * va = pa.vertexList;

		float iX, iY;

		for (int i = 0; i < nva; ++i)
		{
			// Transform local to global coordinates
			iX = va[i].x + pa.x;
			iY = va[i].y + pa.y;
			if (Collision(Point(iX,iY), pb))
				return true;
		}

		int nvb    = pb.vertexCount;
		Point * vb = pb.vertexList;

		for (int i = 0; i < nvb; ++i)
		{
			// Transform local to global coordinates
			iX = vb[i].x + pb.x;
			iY = vb[i].y + pb.y;
			if (Collision(Point(iX,iY), pa))
				return true;
		}
	}

	return false;
}

/**********************************************************************************/

bool SceneManager::Collision(Circle& c, Rect& r)
{
	float cx = c.x;
	float cy = c.y;
	float cr = c.GetRadius();
	
	float left   = r.GetLeft();
	float top    = r.GetTop();
	float right  = r.GetRight();
	float bottom = r.GetBottom();

	Point CircleCenter = Point(cx,cy);

	// Circle center is NE of rectangle
	if (cy <= top && cx >= right)
	{
		if(Point(right,top).Distance(CircleCenter) < cr)
			return true;
	}
	// Circle center is NW of rectangle
	if (cy <= top && cx <= left)
	{
		if(Point(left,top).Distance(CircleCenter) < cr)
			return true;
	}
	// Circle center is SW of rectangle
	if (cy >= bottom && cx <= left)
	{
		if(Point(left,bottom).Distance(CircleCenter) < cr)
			return true;
	}
	// Circle center is SE of rectangle
	if (cy >= bottom && cx >= right)
	{
		if(Point(right,bottom).Distance(CircleCenter) < cr)
			return true;
	}
	// Circle center is N of rectangle
	if (cx > left && cx < right && cy < top)
	{
		if(cy + cr >= top)
			return true;
	}
	// Circle center is E of rectangle
	if (cy > top && cy < bottom && cx > right)
	{
		if(cx - cr <= right)
			return true;
	}
	// Circle center is S of rectangle
	if (cx > left && cx < right && cy > bottom)
	{
		if(cy - cr <= bottom)
			return true;
	}
	// Circle center is W of rectangle
	if (cy > top && cy < bottom && cx < left)
	{
		if(cx + cr >= left)
			return true;
	}
	// Circle center is inside rectangle
	if (cx >= left && cx <= right && cy >= top && cy <= bottom)
	{
		return true;
	}

	// no colision detected
	return false;
}

/**********************************************************************************/

bool SceneManager::Collision(Point& p, Circle& c)
{
	if (p.Distance(Point(c.x, c.y)) <= c.GetRadius())
		return true;
	else 
		return false;
}

/**********************************************************************************/

bool SceneManager::Collision(Point& p, Rect& r)
{
	if (p.x >= r.GetLeft() && p.x <= r.GetRight() && p.y >= r.GetTop() && p.y <= r.GetBottom())
		return true;
	else
		return false;
}

/**********************************************************************************/

bool SceneManager::Collision(Point& p, Poly& pol)
{
	// if point collide with poly's bounding box,
	// proceed to deeper, slower, investigation
	// otherwise there is no collision
	if (Collision(p, pol.GetBBox()))
	{
		bool crossings = false;
		int nvertex    = pol.vertexCount;
		Point * vertex = pol.vertexList;

		float Ix, Iy, Jx, Jy;

		for (int i = 0, j = nvertex-1; i < nvertex; j = i++)
		{
			// Transform local to global coordinates
			Ix = vertex[i].x + pol.x;
			Iy = vertex[i].y + pol.y;
			Jx = vertex[j].x + pol.x;
			Jy = vertex[j].y + pol.y;
			
			if (((Iy < p.y) && (Jy >= p.y)) || ((Jy < p.y) && (Iy >= p.y)))
				if (Jx - (((Jy-p.y)*(Jx-Ix))/(Jy-Iy)) < p.x)
					crossings = !crossings;
		}

		return crossings;
	}

	return false;
}

/**********************************************************************************/

bool Collision(Point& p, Sprite& s);

/**********************************************************************************/

bool SceneManager::Collision(Rect& r, Poly& pol)
{
	// if rectangle collide with poly's bounding box,
	// proceed to deeper, slower, investigation
	// otherwise there is no collision
	if (Collision(r, pol.GetBBox()))
	{
		// get rectangle corners
		Point corners[4] = 
		{
			Point(r.GetLeft(), r.GetTop()),
			Point(r.GetRight(), r.GetTop()),
			Point(r.GetRight(), r.GetBottom()),
			Point(r.GetLeft(), r.GetBottom())
		};

		// verify if corner is inside poly
		for (int i = 0; i < 4; ++i)
			if (Collision(corners[i], pol))
				return true;

		// get polygon vertices
		int nv    = pol.vertexCount;
		Point * v = pol.vertexList;

		float iX, iY;

		// verify is vertices are inside rectangle
		for (int i = 0; i < nv; ++i)
		{
			// Transform local to global coordinates
			iX = v[i].x + pol.x;
			iY = v[i].y + pol.y;
			if (Collision(Point(iX,iY), r))
					return true;
		}
	}

	return false;
}

/**********************************************************************************/

bool SceneManager::Collision(Circle& c, Poly& pol)
{
	// if circle collide with poly's bounding box,
	// proceed to deeper, slower, investigation
	// otherwise there is no collision
	if (Collision(c, pol.GetBBox()))
	{
		// get polygon vertices
		int nv    = pol.vertexCount;
		Point * v = pol.vertexList;

		float iX, iY;

		// verify is vertices are inside circle
		for (int i = 0; i < nv; ++i)
		{
			// Transform local to global coordinates
			iX = v[i].x + pol.x;
			iY = v[i].y + pol.y;
			if (Collision(Point(iX,iY), c))
					return true;
		}
	}

	return false;

}

/**********************************************************************************/

void SceneManager::RegisterStatic(GameObject &obj)
{
	// put object in the end of the staticGameObjs's list
	staticGameObjs.push_back(&obj);
}

/**********************************************************************************/

void SceneManager::RegisterMoving(GameObject &obj)
{
	// put object in the end of the movingGameObjs's list
	movingGameObjs.push_back(&obj);
}

/**********************************************************************************/

void SceneManager::UnRegisterStatic(GameObject &obj)
{
	// remove object from the movingGameObjs's list
	staticGameObjs.remove(&obj);
}

/**********************************************************************************/

void SceneManager::UnRegisterMoving(GameObject &obj)
{
	// remove object from the movingGameObjs's list
	movingGameObjs.remove(&obj);
}

/**********************************************************************************/

bool SceneManager::CollisionDetection()
{
	list<GameObject*>::iterator start, end;

	// empty collisions container
	collisions.clear();

	// if there are at least two moving objects, test collision between them
	if (movingGameObjs.size() >= 2)
	{
		end = movingGameObjs.end();
		end--;

		// test collision between all pairs of moving objects
		for (objI = movingGameObjs.begin(); objI != end; ++objI)
		{
			start = objI;

			for (objJ = ++start; objJ != movingGameObjs.end(); ++objJ)
			{
				if (Collision(**objI, **objJ))
				{
					collisions.push_back(pair<GameObject*,GameObject*>(*objI, *objJ));
				}
			}
		}
	}

	// test collision between moving and static objects
	for (objI = movingGameObjs.begin(); objI != movingGameObjs.end(); ++objI)
	{
		for (objJ = staticGameObjs.begin(); objJ != staticGameObjs.end(); ++objJ)
		{
			if (Collision(**objI, **objJ))
			{
				collisions.push_back(pair<GameObject*,GameObject*>(*objI, *objJ));
			}
		}
	}
	
	return (collisions.empty()? false : true);
}

/**********************************************************************************/

pair<GameObject*,GameObject*> SceneManager::OnCollisionObjects()
{
	if (collisions.empty())
	{
		return pair<GameObject*,GameObject*>(nullptr,nullptr);
	}
	else
	{	
		pair<GameObject*,GameObject*> oneCollisionPair = collisions.back();
		collisions.pop_back();
		return oneCollisionPair;
	}
}

/**********************************************************************************/